package piwan;

import java.awt.AWTEvent;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.awt.event.MouseWheelEvent;
import java.awt.event.MouseWheelListener;
import java.awt.event.WindowEvent;
import java.util.Queue;
import java.util.concurrent.ArrayBlockingQueue;

import javax.media.opengl.GLCanvas;

import sun.awt.WindowClosingListener;

public class EventDispatcher implements KeyListener, MouseMotionListener, MouseListener, MouseWheelListener, WindowClosingListener {
	public EventDispatcher( GLCanvas component, EventHandler handler ) {
		canvas = component;
		hdl = handler;
		enabled = false;
		evq = new ArrayBlockingQueue<DispatchedEvent>(1024);
	}
	
	public void enable() {
		if(!enabled) {
			synchronized(this) {
				canvas.addKeyListener(this);
				canvas.addMouseListener(this);
				canvas.addMouseMotionListener(this);
				canvas.addMouseWheelListener(this);
			}
			enabled = true;
		}
	}
	
	public void disable() {
		if(enabled) {
			synchronized(this) {
				canvas.removeKeyListener(this);
				canvas.removeMouseListener(this);
				canvas.removeMouseMotionListener(this);
				canvas.removeMouseWheelListener(this);
				evq.clear();
			}
			enabled=false;
		}
	}
	
	public void dispatch() {
		synchronized(this) {
			while(!evq.isEmpty()) {
				DispatchedEvent ev = evq.poll();
				if( ev.hasType(EventType.KEYPRESS) )
					hdl.onKeyPressed( (KeyEvent)ev.getEvent() );
				else if( ev.hasType(EventType.KEYREL) )
					hdl.onKeyReleased( (KeyEvent)ev.getEvent() );
				else if( ev.hasType(EventType.MOTION) )
					hdl.onMouseMotion( (MouseEvent)ev.getEvent() );
				else if( ev.hasType(EventType.WHEEL ) )
					hdl.onMouseWheel( (MouseWheelEvent)ev.getEvent() );
				else if( ev.hasType(EventType.BUTPRESS))
					hdl.onMousePressed( (MouseEvent)ev.getEvent() );
				else if( ev.hasType(EventType.BUTREL))
					hdl.onMouseReleased( (MouseEvent)ev.getEvent() );
			}
		}
		
	}
	
	// Event Queue
	private enum EventType {
		KEYPRESS,
		KEYREL,
		BUTPRESS,
		BUTREL,
		MOTION,
		WHEEL,
		QUIT
	}
	
	static private class DispatchedEvent {	
		public DispatchedEvent(EventType eventType, AWTEvent event) {
			this.event = event;
			type = eventType;
		}
		
		public AWTEvent		getEvent() { return event; }		
		public boolean		hasType( EventType checkFor ) { return type == checkFor; }
		
		private EventType	type;
		private AWTEvent	event;
	}

	// Translate AWTEvents to DispatchedEvents and add them to the queue
	public void keyPressed(KeyEvent e) {
		if(!e.isConsumed()) evq.add( new DispatchedEvent(EventType.KEYPRESS, e) );
	}
	public void keyReleased(KeyEvent e) {
		if(!e.isConsumed()) evq.add( new DispatchedEvent(EventType.KEYREL, e) );
	}
	public void mousePressed(MouseEvent e) {
		if(!e.isConsumed()) evq.add( new DispatchedEvent(EventType.BUTPRESS, e) );
	}
	public void mouseReleased(MouseEvent e) {
		if(!e.isConsumed()) evq.add( new DispatchedEvent(EventType.BUTREL, e) );
	}
	public void mouseWheelMoved(MouseWheelEvent e) {
		if(!e.isConsumed()) evq.add( new DispatchedEvent(EventType.WHEEL, e) );
	}
	public void mouseDragged(MouseEvent e) {
		if(!e.isConsumed()) evq.add( new DispatchedEvent(EventType.MOTION, e) );
	}

	public void mouseMoved(MouseEvent e) {
		if(!e.isConsumed()) evq.add( new DispatchedEvent(EventType.MOTION, e) );
	}

	// These events are ignored
	public void keyTyped(KeyEvent pass) {}
	public void mouseClicked(MouseEvent pass) {}
	public void mouseEntered(MouseEvent pass) {}
	public void mouseExited(MouseEvent pass) {}
	
	// On closing disable() so we don't have any dangling events
	public RuntimeException windowClosingDelivered(WindowEvent arg0) { return null; }
	public RuntimeException windowClosingNotify(WindowEvent arg0) {
		disable();
		return null;
	}
	
	private GLCanvas		canvas = null;
	private EventHandler	hdl = null;
	private boolean			enabled = false;
	
	private Queue<DispatchedEvent> evq = new ArrayBlockingQueue<DispatchedEvent>(512);
	

	

	
}